home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue33 / intrposr / ipunit2.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1998-03-04  |  14.1 KB  |  472 lines

  1. unit ipunit2;
  2. //////////////////////////////////////////////////////////////////
  3. //
  4. // Demonstration unit for using Interposer Classes to add functionality
  5. // to the TForm class.
  6. //
  7. // ⌐Stephen Posey -- slposey@concentric.net
  8. // Written for The Delphi Magazine
  9. //
  10. //////////////////////////////////////////////////////////////////
  11. // References:
  12. // The code for drawing the gradient title bar
  13. // is adapted from C++ Builder code in:
  14. //    Miano, J.; Cabanski, T.; & Howe, H. (1997).
  15. //    The Waite Group's Borland C++ Builder How-To.
  16. //    Corte Madera, California, USA: Waite Group Press.
  17. //    ISBN: 1-57169-109-X.
  18. //
  19. // The code for accepting Drag and Drop from File Manager/Explorer
  20. // is adapted from code in
  21. //    Rubenking, N. (1996).
  22. //    Delphi Programming Problem Solver.
  23. //    Foster City, California, USA: IDG Books.
  24. //    ISBN: 1-56884-795-5
  25. //
  26. // The code to make the Enter Key "Tab" among controls
  27. // is adapted from code in
  28. //    Frerking, G.; Wallace, N.; & Niddery, W. (1995).
  29. //    The Waite Group's Borland Delphi How-To.
  30. //    Corte Madera, California, USA: Waite Group Press.
  31. //    ISBN: 1-57169-019-0.
  32.  
  33.  
  34.  
  35. interface
  36.  
  37. uses
  38.   Windows, Messages, SysUtils, ShellAPI, Classes, Graphics,
  39.   Controls, Forms, Dialogs, StdCtrls;
  40.  
  41. type
  42.  
  43.   TForm = class(Forms.TForm)
  44.   // TForm "Interposer" Class adding features to the basic TForm
  45.   // including:
  46.   //   1) a custom gradient filled caption bar
  47.   //   2) a simple "About Box"
  48.   //   3) Enter key processing
  49.   //   4) Accepting Drag and Drop from File Manager/Explorer
  50.  
  51.   // This interposer form class could be placed into its own unit so it
  52.   // could act as ancestor for many form; the new unit name would just
  53.   // have to appear AFTER the Forms unit in this forms "uses" clause
  54.   private
  55.   // Support fields for Custom TitleBar drawing
  56.     WindowCanvas : TCanvas ;
  57.     TitleBarRect : TRect ;
  58.     FinalCaptionColor : TColor ;
  59.     FinalRedIntensity,
  60.     FinalGreenIntensity,
  61.     FinalBlueIntensity : byte ;
  62.     IsNT35 : boolean ;
  63.  
  64.   // Enter key acts like Tab?
  65.     FEnterTab : boolean ;
  66.  
  67.   protected
  68.   // Support routines for custom title bar drawing
  69.     function CanDrawCustomTitleBar : boolean ;
  70.     procedure CalculateTitleBarRect ;
  71.     procedure DrawGradient ;
  72.     procedure DrawIcon ;
  73.     procedure DrawCaptionString ;
  74.  
  75.   // Support routines for Drag and Drop
  76.     procedure ProcessDragDrop( Drop: THandle ; Min : boolean ) ;
  77.     procedure DragDropFileAction( FileName : string ; where : TPoint ) ; virtual ;
  78.  
  79.   // Message Handlers
  80.     procedure WMNCActivate( var Msg : TWMNCActivate ) ;
  81.       message WM_NCACTIVATE ;
  82.  
  83.     procedure WMNCPaint( var Msg : TMessage ) ;
  84.       message WM_NCPAINT ;
  85.  
  86.     procedure WMDropFiles( var Msg : TWMDropFiles ) ;
  87.       message WM_DROPFILES ;
  88.  
  89.   // Overridden methods
  90.     constructor Create(AOwner: TComponent); override;
  91.     destructor Destroy ; override ;
  92.     procedure Resize ; override ;
  93.     procedure KeyDown(var Key: Word; Shift: TShiftState); override ;
  94.  
  95.   public
  96.     procedure AboutBox ;
  97.  
  98.   published
  99.     // Ideally this property would appear in the Object
  100.     // Inspector, but this technique doesn't support that
  101.     property EnterTab : boolean
  102.       read FEnterTab
  103.       write FEnterTab ;
  104.   end ;
  105.  
  106. type
  107.   TDemoForm = class(TForm)  // ancestor is Interposer TForm!
  108.     Button1: TButton;
  109.     ListBox1: TListBox;
  110.     Edit1: TEdit;
  111.     Edit2: TEdit;
  112.     Button2: TButton;
  113.     Label1: TLabel;
  114.     Label2: TLabel;
  115.     Label3: TLabel;
  116.     procedure Button1Click(Sender: TObject);
  117.     procedure Button2Click(Sender: TObject);
  118.     procedure FormShow(Sender: TObject);
  119.   private
  120.     { Private declarations }
  121.   public
  122.     { Public declarations }
  123.     procedure DragDropFileAction( FileName : string ; Where : TPoint ) ; override ;
  124.   end;
  125.  
  126. var
  127.   DemoForm: TDemoForm;
  128.  
  129. implementation
  130.  
  131. {$R *.DFM}
  132.  
  133. procedure GetTitleBarFont( TheFont : TFont  ) ;
  134. var
  135.   TNCM: TNonClientMetrics ;
  136. begin
  137.   TNCM.cbSize := SizeOf( TNonClientMetrics ) ;
  138.   SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, @TNCM, 0 ) ;
  139.   TheFont.Handle := CreateFontIndirect( TNCM.lfCaptionFont ) ;
  140. end;
  141.  
  142. function GetTitleBarFontColor( IsActive : boolean ) : TColor ;
  143. begin
  144.   if IsActive then
  145.     Result :=  TColor ( GetSysColor( COLOR_CAPTIONTEXT    ))
  146.   else
  147.     Result :=  TColor ( GetSysColor( COLOR_INACTIVECAPTIONTEXT    )) ;
  148. end;
  149.  
  150. //
  151. // Interposer TForm's Support routines for Drag and Drop
  152. //
  153. procedure TForm.ProcessDragDrop( Drop: THandle ; Min : boolean ) ;
  154. // generic Drag and Drop processing loop
  155. var
  156.   j : word ;
  157.   Buffer : array [0..255] of char ;
  158.   TP : TPoint ;
  159. begin
  160.   DragQueryPoint( Drop, TP ) ;                  // get position of drop
  161.   for j := 0 to DragQueryFile( Drop, Cardinal( -1 ), nil, 255 ) - 1 do
  162.   begin
  163.     DragQueryFile( Drop, j, Buffer, 80 ) ;        // get next dropped filename
  164.     DragDropFileAction( StrPas( Buffer ), TP ) ;  // Call overridden Drag Drop processor method
  165.   end { for } ;
  166.   DragFinish( Drop ) ;
  167. end;
  168.  
  169. procedure TForm.DragDropFileAction( FileName : string ; where : TPoint ) ;
  170. // virtual do-nothing place holder for descendants' processing of
  171. // Dropped filenames
  172. begin
  173.   //Do nothing
  174.   Exit ;
  175. end;
  176.  
  177. //
  178. // Interposer TForm's Support routines for custom title bar drawing
  179. //
  180. function TForm.CanDrawCustomTitleBar : boolean ;
  181. // Sometimes don't want to perform the gradient fill, this
  182. // encapsulates those situations
  183. begin
  184.   if ( WindowCanvas = nil )       // WMNCPaint might be called AFTER destructor
  185.     or (not Application.Active)   // WM_NCPAINT mesage may arrive when form is inactive
  186.     or (Width < 150)              // Gradient looks poor at very small widths
  187.     or (IsNT35)                   // NT 3.5 uses Win3 interface, would require
  188.                                   // special alternate processing, not provided here
  189.   then
  190.   begin
  191.     Result := FALSE ;
  192.     Exit ;
  193.   end;
  194.  
  195.   // Gradient looks poor if it's from black to a very light color.
  196.   // Word (e.g.) actually shades from White instead of Black for
  197.   // light caption colors.  Here we just don't shade for light colors.
  198.   FinalCaptionColor := TColor( GetSysColor( COLOR_ACTIVECAPTION )) ;
  199.   FinalRedIntensity   := GetRValue( FinalCaptionColor ) ;
  200.   FinalGreenIntensity := GetGValue( FinalCaptionColor ) ;
  201.   FinalBlueIntensity  := GetBValue( FinalCaptionColor ) ;
  202.  
  203.   // Is at least on of the RGB values in the dark range (< 128)?
  204.   // If not, don't draw gradient
  205.   if (FinalRedIntensity < 128)
  206.     or (FinalGreenIntensity < 128)
  207.     or (FinalBlueIntensity < 128)
  208.   then
  209.     Result := TRUE
  210.   else
  211.     Result := FALSE ;
  212. end;
  213.  
  214. procedure TForm.CalculateTitleBarRect ;
  215. // Determine dimensions of titlebar area
  216. // The Left, Top, and Bottom must be calculate precisely
  217. // The right value is arbitrary, but meant to be large enough
  218. // to avoid the Min/Max/Exit buttons
  219. begin
  220.   TitleBarRect := Rect(
  221.     GetSystemMetrics(SM_CXFRAME),
  222.     GetSystemMetrics(SM_CYFRAME),
  223.     Width - 4 * GetSystemMetrics(SM_CXSIZE),  // stay away from Min/Max/Exit btns.
  224.     GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CYCAPTION) - 1
  225.   ) ;
  226. end;
  227.  
  228. procedure TForm.DrawGradient ;
  229. // Gradient shading involves gradually increasing RGB color values from
  230. // 0 (black) to the final RGB value of the caption color.  The increment
  231. // values are multiplied by 0, 1..31 to create 32 shades.
  232. // The Final<RGB>Intensity values are calculated in CanDrawCustomTitleBar.
  233. var
  234.   RedIncrement, GreenIncrement, BlueIncrement, SectionWidth  : byte ;
  235.   Section : TRect ;
  236.   FillColor : TColor ;
  237.   j : integer ;
  238. begin
  239.   // Maximum intensity is 255,
  240.   // adding 1 makes Final<RGB>Intensity evenly divisible by 32.
  241.   RedIncrement   := ( FinalRedIntensity   + 1 ) div 32 ;
  242.   GreenIncrement := ( FinalGreenIntensity + 1 ) div 32 ;
  243.   BlueIncrement  := ( FinalBlueIntensity  + 1 ) div 32 ;
  244.  
  245.   Section := TitleBarRect ;
  246.   // Section width is the width of each gradient "section".
  247.   // Titlebar will contain 36 gradient sections, 5 will be Black
  248.   // on the right to account for the Min/Max/Exit buttons.
  249.   // The remaining 31 sections will shade between Black and the
  250.   // titlebar color.
  251.   SectionWidth := ( TitleBarRect.Right - TitleBarRect.Left ) div 36 ;
  252.  
  253.   FillColor := clBlack ;
  254.  
  255.   Section.Right := Section.Left + 5 * SectionWidth ;  // 5 sections of black
  256.  
  257.   // configure the brush
  258.   WindowCanvas.Brush.Color := FillColor ;
  259.   WindowCanvas.Brush.Style := bsSolid ;
  260.  
  261.   // fill in the black sections
  262.   WindowCanvas.FillRect( Section ) ;
  263.  
  264.   // move section over
  265.   Section.Left := Section.Left + ( 5 * SectionWidth ) ;
  266.  
  267.   for j := 1 to 31 do    //iterate through shades between Black and TitleBar color
  268.   begin
  269.     Section.Right := Section.Left + SectionWidth ;  // size the section
  270.     // set the brush color and fill the section
  271.     FillColor := RGB( RedIncrement * j, GreenIncrement * j, BlueIncrement * j ) ;
  272.     WindowCanvas.Brush.Color := FillColor ;
  273.     WindowCanvas.FillRect( Section ) ;
  274.  
  275.     Section.Left := Section.Left + SectionWidth ;  // shift section over
  276.   end { for };
  277. end;
  278.  
  279. procedure TForm.DrawIcon ;
  280. // Redraw the System Menu button because the gradient drawing just painted
  281. // over it with Black.
  282. var
  283.   IconWidth, IconHeight : integer ;
  284.   Section : TRect ;
  285. begin
  286.   // Width and height of "small icon"
  287.   IconWidth := GetSystemMetrics( SM_CXSMICON ) ;
  288.   IconHeight := GetSystemMetrics( SM_CYSMICON ) ;
  289.  
  290.   // where does System Menu button fit?
  291.   Section := Rect(
  292.     TitleBarRect.Left + 2,
  293.     TitleBarRect.Top + 1,
  294.     TitleBarRect.Left + IconWidth,
  295.     TitleBarRect.Top + IconHeight
  296.   ) ;
  297.  
  298.   // Redraw the button
  299.   DrawIconEx( WindowCanvas.Handle, Section.Left, Section.Top,
  300.     Application.Icon.Handle,IconWidth, IconHeight, 0, 0, DI_NORMAL ) ;
  301. end;
  302.  
  303. procedure TForm.DrawCaptionString ;
  304. // Redraw Caption string.  Working with a Temporary copy of the
  305. // Caption from the Caption property avoids flicker.
  306. var
  307.   R : TRect ;
  308.   TempCap : string ;
  309. begin
  310.   // Area of TitleBar for caption
  311.   R := Rect( TitleBarRect.Left + 2 + 16 + 4, TitleBarRect.Top,
  312.     TitleBarRect.Right - 20, TitleBarRect.Bottom ) ;
  313.  
  314.   TempCap := Caption ;
  315.  
  316.   // Transparent mode so text doesn't wipe out Gradient
  317.   SetBKMode( WindowCanvas.Handle, TRANSPARENT ) ;
  318.  
  319.   // Get info on title bar font (Face, Color, Style, etc.) from system 
  320.   GetTitleBarFont( WindowCanvas.Font ) ;
  321.   WindowCanvas.Font.Color := GetTitleBarFontColor( Active ) ;
  322.  
  323.   // Redraw the caption
  324.   DrawText( WindowCanvas.Handle, PChar( TempCap ), Length( Caption ),
  325.     R, DT_SINGLELINE or DT_VCENTER ) ;
  326. end;
  327.  
  328. //
  329. // Interposer TForm's Message Handlers
  330. //
  331. procedure TForm.WMDropFiles( var Msg : TWMDropFiles ) ;
  332. begin
  333.   ProcessDragDrop( Msg.Drop, FALSE ) ;
  334.   Msg.Result := 0 ;
  335. end;
  336.  
  337. procedure TForm.WMNCActivate( var Msg : TWMNCActivate ) ;
  338. var
  339.   PaintMsg : TMessage ;
  340. begin
  341.   Msg.Result := Cardinal( TRUE ) ;  // always handle this message
  342.   if not Msg.Active then            // If form inactive do default drawing
  343.   begin
  344.     DefWindowProc( Handle, Msg.Msg, LongInt( Msg.Active ), 0 ) ;
  345.     // Code for inactive titlebar drawing may be added here
  346.     // Example leaves inactive caption at default appearance
  347.     Exit ;
  348.   end;
  349.  
  350.   // if Active window, do WMNCPaint
  351.   PaintMsg.Msg := Msg.Msg ;                   // build message
  352.   PaintMsg.WParam := LongInt( Msg.Active ) ;
  353.   WMNCPaint( PaintMsg ) ;                     // call it
  354. end;
  355.  
  356. procedure TForm.WMNCPaint( var Msg : TMessage ) ;
  357. var
  358.   WindowDC : HDC ;
  359. begin
  360.   // Use default processing to draw min/max/close buttons, menu, and the
  361.   // frame.  The caption bar is drawn too, but we paint over that later.
  362.   DefWindowProc( Handle, Msg.Msg, Msg.WParam, Msg.LParam ) ;
  363.  
  364.   // check for any reason not to draw the custom caption
  365.   if CanDrawCustomTitleBar = FALSE then
  366.     Exit ;
  367.  
  368.   // Create a DC for the entire form window, then assign
  369.   // the handle to the window canvas
  370.   WindowDC := GetWindowDC( Handle ) ;
  371.   WindowCanvas.Handle := WindowDC ;
  372.  
  373.   CalculateTitleBarRect ;        // calculate dimensions of titlebar
  374.   DrawGradient ;                 // draw gradient fill
  375.   DrawIcon ;                     // redraw system menu button
  376.   DrawCaptionString ;            // draw the caption
  377.  
  378.   ReleaseDC( Handle, WindowDC ) ; // free resources
  379.   WindowCanvas.Handle := 0 ;
  380. end;
  381.  
  382. //
  383. // Interposer TForm's Overridden methods
  384. //
  385. constructor TForm.Create( AOwner : TComponent ) ;
  386. var
  387.   Ver : TOSVersionInfo ;
  388. begin
  389.   inherited Create( AOwner ) ;
  390.   // Drag and Drop processing Setup
  391.   DragAcceptFiles( Handle, TRUE ) ;
  392.  
  393.   // custom title bar drawing setup
  394.   WindowCanvas := TCanvas.Create ;
  395.  
  396.   // NT 3.5 uses Win 3.1 interface, needs custom handling
  397.   Ver.dwOSVersionInfoSize := SizeOf(TOSVersionInfo) ;
  398.   GetVersionEx( Ver ) ;
  399.   if ( Ver.dwMajorVersion < 4 ) then
  400.     IsNT35 := TRUE
  401.   else
  402.     IsNT35 := FALSE ;
  403.  
  404.   // Default to normal Enter key behavior
  405.   FEnterTab := FALSE ;
  406. end;
  407.  
  408. destructor TForm.Destroy ;
  409. begin
  410.   WindowCanvas.Free ;
  411.   WindowCanvas := nil ;
  412.   inherited Destroy ;
  413. end;
  414.  
  415. procedure TForm.Resize ;
  416. begin
  417.   inherited Resize ;
  418.   Perform( WM_NCPAINT, 1, 0 ) ;
  419. end;
  420.  
  421. procedure TForm.KeyDown(var Key: Word; Shift: TShiftState);
  422. begin
  423.   if FEnterTab then
  424.   if Key = VK_RETURN then
  425.   begin
  426.     Key := 0 ;
  427.     if not ( ssShift in Shift ) then
  428.       Perform( WM_NEXTDLGCTL, 0, 0 )
  429.     else
  430.       Perform( WM_NEXTDLGCTL, 1, 0 ) ;
  431.   end ;
  432.   inherited KeyDown( Key, Shift ) ;
  433. end;
  434.  
  435. procedure TForm.AboutBox ;
  436. // Simple About box
  437. begin
  438.   MessageBeep( MB_ICONEXCLAMATION ) ;
  439.   MessageBox( Handle, 'TForm Interposer Class Demonstration', 'About...',
  440.     MB_OK or MB_ICONEXCLAMATION or MB_TASKMODAL ) ;
  441. end;
  442.  
  443.  
  444. //
  445. // descendant DemoForm's Methods
  446. //
  447. procedure TDemoForm.Button1Click(Sender: TObject);
  448. begin
  449.   AboutBox ;
  450. end;
  451.  
  452. procedure TDemoForm.DragDropFileAction( FileName : string ; Where : TPoint ) ;
  453. // Descendant method for DragDrop action
  454. begin
  455.   ListBox1.Items.Add( Format( '%s dropped at (%d, %d)', [FileName, Where.X, Where.Y] )) ;
  456. end;
  457.  
  458. procedure TDemoForm.Button2Click(Sender: TObject);
  459. begin
  460.   Close ;
  461.   Application.Terminate ;
  462. end;
  463.  
  464. procedure TDemoForm.FormShow(Sender: TObject);
  465. begin
  466.   EnterTab := TRUE ;
  467. end;
  468.  
  469. end.
  470.  
  471.  
  472.